package com.dashang.feecenter.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.dashang.feecenter.bean.JoinQuery;
import com.dashang.feecenter.bean.ResponseBean;
import com.dashang.feecenter.bean.ResponseList;
import com.dashang.feecenter.client.BaseCenterClient;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.BeanUtils;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * com.dashang.feecenter.util dashang-center
 *
 * @author duanxd
 * @version 1.0
 * @since 2021/11/17 09:15
 */

@Aspect
@Configuration
@EnableDiscoveryClient
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class DifferentServerJoinQueryAspect {

    /**
     * 处理须要在服务间进行连表查询
     * @param joinPoint
     * @return
     */
    @Around(value = "@annotation(com.dashang.feecenter.bean.NeedJoinQuery)")
    public  Object joinQueryAfterAdvice(ProceedingJoinPoint joinPoint){
        Object returnValue = null;
        try {
            returnValue = joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        if (returnValue == null){
            return returnValue;
        }

        // 处理结果
        handlerJoinQuery(returnValue);

        return returnValue;
    }

    private <T> void handlerJoinQuery(Object returnValue){
        // 返回实体的 class对象
        Class<T> aClass ;
        List<T> listValue = new ArrayList<T>() ;
        if (returnValue instanceof List){
            listValue = (List<T>) returnValue;
            // 获取泛型的 class 对象
            if (listValue.size() == 0){
                return;
            }else {
                aClass = (Class<T>) listValue.get(0).getClass();
            }
        }else {
            aClass = (Class<T>)returnValue.getClass();
        }

        // 找出须要连表查询的属性
        Map<Field, JoinQuery> fieldMap = CommonUtils.getFieldsAnnotation(aClass,JoinQuery.class);
        // 对每一个属性进行处理
        for (Map.Entry<Field, JoinQuery> stringAnnotationEntry : fieldMap.entrySet()) {
            Field field = stringAnnotationEntry.getKey();
            // 属性名
            String fieldName = field.getName();
            // 注解对象
            JoinQuery annotation =  stringAnnotationEntry.getValue();
            String serverId = annotation.serverId();
            String tableName = annotation.tableName();
            String guidName = annotation.guidName();

            /**
             * 根据serverId获取feign实例，
             * 1.若是是用@FeignClient注解生成的代理类，那么实例在容器中的名字为 serverId + “FeignClient”
             * 2.若是是自定义的Feign 代理类，那么这里还须要改进。
             *      若是在容器中读取不到，应该到一个Feign集合中去读取，若是仍是读取不到，抛出异常
             *      Feign集合应该是开发人员自定义代理类时加入进去
             */
            BaseCenterClient crudCommonFeignClient = (BaseCenterClient)SpringContextUtil.getBean(serverId+"FeignClient" );

            // 返回集合
            if (returnValue instanceof List){
                /** guid集合 */
                List<String> guidList = new ArrayList<>();
                /** key：主键值，value：对应的对象 */
                Map<String,Object> guidAndObj = new HashMap<>();
                for (T obj : listValue) {
                    // 获取须要关联查询的对象
                    Object get = CommonUtils.optFieldValue(obj, fieldName, "get");
                    // 获取关联查询对象的主键值
                    String o = (String)CommonUtils.optFieldValue(get, guidName, "get");
                    guidList.add(o);
                    guidAndObj.put(o,get);
                }
                String s;
                try {
                    // 去其余服务进行数据请求
                    s = crudCommonFeignClient.data(tableName, CommonUtils.concatBySeparator(guidList,","));
                }catch (Exception e){
                    e.printStackTrace();
                    // 发生错误时，进行下一个属性的查询
                    continue;
                }


                // 处理数据
                ResponseList<T> responseBean = JSON.parseObject(s, new TypeReference<ResponseList<T>>(field.getType()) {});
                if (responseBean.isSuccess()){
                    List<T> datas = responseBean.getDatas();
                    if (null != datas){
                        for (T data : datas) {
                            // 获取关联查询对象的主键值，这是得到的guid
                            String retGuid = (String)CommonUtils.optFieldValue(data, guidName, "get");
                            // 根据guid 获取对应的返回值中的对象
                            Object queryObj = guidAndObj.get(retGuid);
                            if (null != queryObj){
                                // 把服务调用得到的值 复制到 查询获得的对象中
                                BeanUtils.copyProperties(data,queryObj);
                            }
                        }
                    }
                }
            }

            // 返回对象
            else {
                // 获取须要关联查询的对象
//                Object get = CommonUtils.optFieldValue(returnValue, fieldName, "get");
                // 获取关联查询对象的主键值
                Object o = CommonUtils.optFieldValue(returnValue, guidName, "get");
                // 去其余服务进行数据请求
                String s = crudCommonFeignClient.data2(tableName, o.toString());

                // 处理获取的结果
                ResponseBean<T> responseBean = JSON.parseObject(s, new TypeReference<ResponseBean<T>>(field.getType()) {});
                T data = responseBean.getData();
                if (responseBean.isSuccess() && null != data){
                    // 把获取到的数据设置到返回值中
                    CommonUtils.optFieldValue(returnValue, fieldName, "set",data);
                }
            }

        }
    }
}
